//ENCRIPTION.v - This is the encription hardware for project 2.
//
// Created By:	Jesse Inkpen
// Date:	18-February-2014
//
// This module has a three fixed values applied to its input port and waits
// untill a go signal to start calculating an RSA encription block
// Use the define 'BITS to set the bit size of the ENCRIPTION.v synthesis.
///////////////////////////////////////////////////////////////////////////

`define BITS 16

module RSA(
	input						clk,		// clock
	input						go,		// hardware go go go
	input	[`BITS-1:0]		m,		// message
	input	[`BITS-1:0]		e,		// exponent
	input	[`BITS-1:0]		n,		// modulous
	input						ready,
	input [`BITS-1:0]		result,
	
	output	reg [`BITS-1:0]		r,		// post processing result
	output	reg			d,				// done signal
	output	reg			start,
	output	reg [`BITS-1:0]	A,
	output	reg [`BITS-1:0]   B,
	output   reg [`BITS-1:0]   M
	);
		
	//****** Parameters **************************
	parameter	RESET			=  16'b0000000000000001,
					INIT			=	16'b0000000000000010,
					INIT_Z		=	16'b0000000000000100,
					WAIT_Z0		=	16'b0000000000001000,
					INIT_P		=	16'b0000000000010000,
					WAIT_P0		=	16'b0000000000100000,
					CHECK_e		=	16'b0000000001000000,
					CALC_P		=	16'b0000000010000000,
					WAIT_P		=	16'b0000000100000000,
					EVAL_Z		=	16'b0000001000000000,
					WAIT_Z		=	16'b0000010000000000,
					UPD_P			=	16'b0000100000000000,
					R_SHFT_EXP	=	16'b0001000000000000,
					END_LOOP		=	16'b0010000000000000,
					WAIT_Zn		=  16'b0100000000000000,
					DONE			=	16'b1000000000000000;
					
	reg [15:0]	State; 
	reg [15:0] 	NextState;

	reg 	[`BITS-1:0]	 z;		// message register
	reg	[`BITS-1:0]  p_old;
	reg	[`BITS-1:0]  p_new;
	reg	[`BITS-1:0]	 exp;		//copy of exp used in modular exponentiation
	reg   [`BITS-1:0]  Nr;
	reg	[`BITS-1:0]  result_in;

   //wire [`BITS*2:0] NR = (1<<(`BITS<<1)) % n ;
	parameter NR = `BITS'd857; // 16 bit

	//Update state
	always @(posedge clk)
	begin
		if(go == 0)
			State	<=	RESET;
		else	
			State	<=	NextState;
	end	
		
	//Next state logic
	always @(*)
	begin
		case (State)
			RESET:		begin
								NextState	=	INIT;		
							end
		
			INIT:			begin
								NextState	=	INIT_Z;		
							end
							
			INIT_Z:		begin
								if(ready == 1)
									NextState	=	WAIT_Z0;
								else
									NextState	=	INIT_Z;		
							end
							
			WAIT_Z0:		begin
								NextState	=	INIT_P;
							end
							
			INIT_P:		begin
								if(ready == 1)
									NextState	=	WAIT_P0;
								else
									NextState	=	INIT_P;		
							end

			WAIT_P0:		begin
								NextState	=	CALC_P;
							end
			
			CHECK_e:		begin
							if(exp != 0)	
								NextState = CALC_P;
							else
								NextState = END_LOOP;
							end			
			
			CALC_P:		begin
							if(ready == 1)
								NextState	=	WAIT_P;
							else
								NextState	=	CALC_P;
							end
							
			WAIT_P:		begin
								NextState	=	EVAL_Z;
							end
			
			EVAL_Z:		begin
								if(ready == 1)
									NextState	=	WAIT_Z;
								else
									NextState	=	EVAL_Z;
							end
							
			WAIT_Z:		begin
								NextState	=	UPD_P;
							end
										
			UPD_P:		begin
								NextState = R_SHFT_EXP;
							end
							
			R_SHFT_EXP:	begin
								NextState = CHECK_e;
							end
							
			END_LOOP:	begin
								if(ready == 1)
									NextState	=	WAIT_Zn;
								else
									NextState	=	END_LOOP;
							end
							
			WAIT_Zn:		begin
								NextState	=	DONE;
							end

			DONE:			begin
								NextState = RESET;
							end								
			
			default:		begin
								NextState = RESET;
							end			
		endcase
	end
		
	//Output logic
	
	always @(State)
	begin
		start = start;
		exp = exp; 
		p_old = p_old;
		p_new = p_new;
		r = r; 
		z = z;
		
		case (State)
			RESET:		begin
								d		= 0;
								start = 0;
							end
							
			INIT:			begin
								exp	=	e;
								d		=	0;
								start = 0;
							end
							
			INIT_Z:		begin
								A		=	`BITS'd1;
								//Nr		=	NR;
								B		=	NR;
								M		=	n;
								start	=	1;	
							end
							
			WAIT_Z0:		begin
								start = 0;
								z		=	result_in;
							end
							
			INIT_P:		begin
								start = 0;
								A		=	m;
								//Nr		=	NR;
								B		=	NR;
								M		=	n;
								start	=	1;
							end	 

			WAIT_P0:		begin
							   start = 0;
								p_old	=	result_in;
							end

			CHECK_e:		begin
								start = 0;
								d		= 0;					
							end
							
			CALC_P:		begin
								A	=	p_old;
								B	=	p_old;
								M	=	n;
								start = 1;
							end

			WAIT_P:		begin
							   start = 0;
								p_new	=	result_in;
							end

			EVAL_Z:		begin
								if(exp[0] == 1)
								begin
									A		=	z;
									B		=	p_old;
									M		=	n;
									start	=	1;
								end	
							end

			WAIT_Z:		begin
							   start = 0;
								z		=	result_in;
							end

			UPD_P:		begin
								start = 0;
								p_old	= p_new;
							end

			R_SHFT_EXP:	begin
								start = 0;
								exp 	= {1'b0,exp[`BITS-1:1]};
							end

			END_LOOP:	begin
								start = 0;
								A		=	`BITS'd1;
								B		=	z;
								M		=	n;
								start	=	1;																
							end

			WAIT_Zn:		begin
							   start = 0;
								z		=	result_in;
							end

			DONE:			begin
								start	= 0;
								r		=	z;	
								d 		= 1;					// yes, flag done set
							end
			
			default:		begin
								start = 0;
								d 		= 0;					// done flag reset								
							end	
		endcase
	end
	
	always @(posedge clk)
	begin
		if(ready == 1)
		begin
			result_in <= result;
		end
	end
	
endmodule
			